<template>
  <div
    class="thumb-zx-xmind-map"
    :style="{ 'background-color': backgroundColor, 'box-shadow': `inset 0px 0px 3px 0px ${shadowBorderColor}, 0px 0px 3px 0px ${shadowBorderColor}, 0px 8px 40px 0px ${shadowBorderColor}` }">
    <div id="thumb-zx-xmind-map"></div>
    <div
      class="thumb-xmind-viewport"
      :style="{
        width: `${viewportScreen.width}px`,
        height: `${viewportScreen.height}px`,
        left: `${viewportBounding.left}px`,
        top: `${viewportBounding.top}px`,
        ...thumbStyle
      }">
      <div class="thumb-radio" :style="thumbStyle"></div>
    </div>
  </div>
</template>

<script setup>
import { select } from 'd3-selection'
import { color, rgb } from 'd3-color'
import { ref, defineProps, defineEmits, computed, nextTick, watch, defineExpose } from 'vue'

let svg = null
let mindContainer = null
let canvasWidth = 0
let canvasHeight = 0
const emits = defineEmits(['move-map'])
const props = defineProps({
  scaleNumber: {
    type: Number,
    default: 1
  },
  boundingClient: {
    type: Object,
    default: () => null
  },
  thumbBorderColor: String,
  backgroundColor: String
})
const scale = 0.1
const couldDragState = ref(false)
const viewportScreen = ref({ width: 0, height: 0 })
const viewportBounding = ref({ left: 0, top: 0 })
const scalceRadio = computed(() => {
  return scale / props.scaleNumber
})

const shadowBorderColor = computed(() => {
  const backgroundColor = props.backgroundColor
  if (!backgroundColor) return
  const { r, g, b } = rgb(backgroundColor).clamp()
  const grayLevel = r * 0.299 + g * 0.587 + b * 0.114
  if (grayLevel < 127) return '#FFFFFF25'
  return color(backgroundColor).darker(0.15).hex()
})

nextTick(() => {
  initThumbMindContainer()
  calcViewportBounding()
  moveViewportBox()
})

/**
 * xmindMap缩略图画布渲染
 */
function initThumbMindContainer () {
  const ele = document.getElementById('thumb-zx-xmind-map')
  canvasWidth = ele.clientWidth
  canvasHeight = ele.clientHeight
  svg = select(ele).append('svg').attr('id', 'thumb-zx-xmind-map-svg')
    .attr('width', canvasWidth)
    .attr('height', canvasHeight)
    .attr('xmlns', 'http://www.w3.org/2000/svg')
    .attr('xmlns:xlink', 'http://www.w3.org/1999/xlink')
  mindContainer = svg
    .append('g')
    .attr('class', 'thumb-map-outter-container')
    .attr('transform', `scale(${scale})`)
  renderThumbXmindMap()
}

function updateThumbXmindMap () {
  renderThumbXmindMap()
}

function renderThumbXmindMap () {
  mindContainer.html(select('.map-outter-container').html())
  if (select('.thumb-map-outter-container .x-mind-root-theme').empty()) return
  const { top: ctop, bottom: cbottom, left: cleft, right: cright } = select('.thumb-map-outter-container').node().getBoundingClientRect()
  const { width, height, left, top } = select('.thumb-map-outter-container .x-mind-root-theme').node().getBoundingClientRect()
  const x1 = width / 2 + left - cleft
  const x2 = cright - (width / 2 + left)
  const y1 = height / 2 + top - ctop
  const y2 = cbottom - (height / 2 + top)
  mindContainer
    .attr('transform', `translate(${(canvasWidth - width) / 2 + (x1 - x2) / 2}, ${(canvasHeight - height) / 2 + (y1 - y2) / 2}) scale(${scale})`)
    .each(function () {
      select(this).selectAll('.x-mind-nodetheme > *:not(.x-mind-node-block)').remove()
      select(this).selectAll('.out-border-desc-input').style('color', 'transparent')
      select(this).selectAll('.mind-map-summarybox foreignObject').remove()
      select(this).selectAll('.mind-map-relationbox .controller-model, .drag-shadow-node').remove()
    })
    .selectAll('g, path, rect, circle, div, p')
    .attr('class', null)
    .attr('id', null)
    .on('click', null).on('mousedown', null).on('mousemove', null).on('mouseleave', null).on('mouseup', null).on('mouseover', null).on('mouseenter', null)
}

function moveViewportBox () {
  select('.thumb-xmind-viewport').on('mouseenter', () => {
    couldDragState.value = false
  }).on('mouseleave', () => {
    couldDragState.value = false
  }).on('mousedown', (event) => {
    event.stopPropagation()
    couldDragState.value = true
  }).on('mousemove', event => {
    if (couldDragState.value) {
      emits('move-map', { x: event.movementX, y: event.movementY })
    }
  }).on('mouseup', () => {
    couldDragState.value = false
  })
}

function calcViewportBounding () {
  const thumbmap = document.querySelector('.thumb-zx-xmind-map')
  const mindmap = document.querySelector('.thumb-map-outter-container')
  const thumbmapInfo = thumbmap?.getBoundingClientRect()
  const mindmapInfo = mindmap?.getBoundingClientRect()
  viewportBounding.value = {
    left: (mindmapInfo?.left - thumbmapInfo?.left) - props.boundingClient.left * scalceRadio.value - 2,
    top: (mindmapInfo?.top - thumbmapInfo?.top) - props.boundingClient.top * scalceRadio.value - 2
  }
}

watch(() => scalceRadio.value, () => {
  nextTick(() => {
    const sourceEle = document.getElementById('zx-xmind-map')
    if (sourceEle) {
      viewportScreen.value = {
        width: Math.ceil(sourceEle.clientWidth * scalceRadio.value),
        height: Math.ceil(sourceEle.clientHeight * scalceRadio.value)
      }
    }
  })
}, { immediate: true })

watch(() => props.boundingClient, () => {
  calcViewportBounding()
}, { deep: true })

const thumbStyle = computed(() => {
  return {
    'border-color': props.thumbBorderColor
  }
})

defineExpose({
  updateThumbXmindMap
})
</script>

<style lang="scss">
.thumb-zx-xmind-map {
  position: fixed;
  top: 0;
  right: 0;
  width: 280px;
  height: 150px;
  user-select: none;
  border-radius: 2px;
  overflow: hidden;
  z-index: 7;
  border-radius: 0 0 0 8px;
  #thumb-zx-xmind-map {
    width: 100%;
    height: 100%;
    pointer-events: none;
  }
  .thumb-xmind-viewport {
    position: absolute;
    border: 2px solid #d53e78;
    left: 0;
    top: 0;
    box-sizing: content-box;
    border-radius: 4px;
    cursor: url(''), auto;
    .thumb-radio {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      border: 2px solid #d53e78;
      right: -5px;
      bottom: -5px;
      position: absolute;
      background: #fff;
    }
  }
}
</style>
